import { dir, state } from "./state";

const { ccclass, property } = cc._decorator;
const gfx = cc['gfx'];
const vertexFmt = new gfx.VertexFormat([
    { name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
    { name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
    { name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_FLOAT32, num: 4, normalize: true },
])

@ccclass
export default class picturePuzzleAssembler extends cc.Assembler {

    floatsPerVert = 5;

    uvOffset = 2;
    colorOffset = 4;
    // 贝塞尔曲线上的节点数
    bezierCount = 30;
    rootSize: cc.Size = cc.size(0, 0);

    public _renderData: cc.RenderData = null;
    public _local: Array<any> = [];
    // public vertexes: Array<cc.Vec2> = [];

    get vertexesCount() {
        return 4 + 4 * this.bezierCount;
    }
    get verticesFloats() {
        return this.floatsPerVert * this.vertexesCount;
    }
    get indicesCount() {
        return (this.vertexesCount - 2) * 3;
    }
    // 列、行、宽度、高度
    colNum: number = 0;
    rowNum: number = 0;
    picWidth: number = 0;
    picHeight: number = 0;
    isTopExtend: state = state.normal;
    isBottomExtend: state = state.normal;
    isLeftExtend: state = state.normal;
    isRightExtend: state = state.normal;
    posOffset: cc.Vec2 = null;
    sprite: cc.Sprite = null;
    ctx: cc.Graphics = null;
    init(comp: cc.Sprite) {
        super.init(comp);
        this._renderData = new cc.RenderData();
        this._renderData.init(this);

        this.initLocal();
        this.initData();
    }
    initLocal() {
        this._local = [];
        // console.log('this.vertexesCount', this.vertexesCount)
        this._local.length = this.vertexesCount;
    }
    initData() {
        let data = this._renderData;
        data.createFlexData(0, this.verticesFloats, this.indicesCount);
    }
    // getVfmt() {
    //     return vertexFmt;
    // }
    getBuffer() {
        //@ts-ignore
        return cc.renderer._handle._meshBuffer;
    }
    // 更新数据
    updateRenderData(sprite) {
        // let frame = sprite._spriteFrame;
        // if (!frame) return;
        // this.packToDynamicAtlas(sprite, frame);

        // console.log('updateRenderData1', sprite.isLoaded)

        if (sprite._vertsDirty) {
            this.updateVerts(sprite);
            this.updateUVs(sprite);
            this.updateIndices(sprite);
            this.updateColor(sprite);

            sprite._vertsDirty = false;
            // console.log('updateRenderData2')
        }
    }
    // 将准备好的顶点数据填充进 VertexBuffer 和 IndiceBuffer
    fillBuffers(comp, renderer) {
        if (renderer.worldMatDirty) {
            this.updateWorldVerts(comp);
        }

        let renderData = this._renderData;
        let vData = renderData.vDatas[0];
        let iData = renderData.iDatas[0];

        let buffer = this.getBuffer(/*renderer*/);
        let offsetInfo = buffer.request(this.vertexesCount, this.indicesCount);

        // buffer data may be realloc, need get reference after request.

        // fill vertices
        let vertexOffset = offsetInfo.byteOffset >> 2,
            vbuf = buffer._vData;

        if (vData.length + vertexOffset > vbuf.length) {
            vbuf.set(vData.subarray(0, vbuf.length - vertexOffset), vertexOffset);
        } else {
            vbuf.set(vData, vertexOffset);
        }

        // fill indices
        let ibuf = buffer._iData,
            indiceOffset = offsetInfo.indiceOffset,
            vertexId = offsetInfo.vertexOffset;             // vertexId是已经在buffer里的顶点数，也是当前顶点序号的基数
        for (let i = 0, l = iData.length; i < l; i++) {
            ibuf[indiceOffset++] = vertexId + iData[i];
        }
    }
    // 获取中间部分的点
    getInnerPoints(direction: dir, stat: state) {
        // 获取两点之间的顶点
        let p0, p1, p2, p3, t;
        let arr = [];
        if (stat == state.normal) {
            // 直线顶点
            let count = (this.bezierCount + 1);
            let normalWidth = (this.picWidth - 1) / count;
            let normalHeight = (this.picHeight - 1) / count;
            for (let i = 0; i < this.bezierCount; i++) {
                t = i / (count - 1);
                let pos = cc.v2(0, 0);
                switch (direction) {
                    case dir.top: {
                        pos = cc.v2(-this.picWidth / 2 + normalWidth + t * this.picWidth, this.picHeight / 2);
                        break;
                    }
                    case dir.bottom: {
                        pos = cc.v2(this.picWidth / 2 - normalWidth - t * this.picWidth, -this.picHeight / 2);
                        break;
                    }
                    case dir.left: {
                        pos = cc.v2(-this.picWidth / 2, -this.picHeight / 2 + normalHeight + t * this.picHeight);
                        break;
                    }
                    case dir.right: {
                        pos = cc.v2(this.picWidth / 2, this.picHeight / 2 - normalHeight - t * this.picHeight);
                        break;
                    }
                    default: {
                        // console.log('默认')
                        pos = cc.v2(this.picWidth / 2, this.picHeight / 2 - normalHeight - t * this.picHeight);
                        break
                    }
                }
                // console.log('count1', t, count, normalWidth, normalHeight)
                arr.push(pos);
            }
        } else {
            let rotation = 30;
            let extendLine = (this.picWidth + this.picHeight) / 2 * 0.45;
            let angleL = 0 + rotation, angleR = 0 - rotation;
            let horizontal_distance = this.picWidth * 0.1;
            let vertical_distance = this.picHeight * 0.1;
            if (stat == state.inner) {
                // 互补角度的两倍
                let agl = (90 - angleL) * 2;
                angleL += agl;
                angleR -= agl;
            }
            // console.log('angle', angleL, angleR);
            switch (direction) {
                case dir.top: {
                    p0 = cc.v2(-horizontal_distance, this.picHeight / 2);
                    p3 = cc.v2(horizontal_distance, this.picHeight / 2);
                    angleL += 0;
                    angleR += 0
                    break;
                }
                case dir.right: {
                    p0 = cc.v2(this.picWidth / 2, vertical_distance);
                    p3 = cc.v2(this.picWidth / 2, -vertical_distance);
                    angleL -= 90;
                    angleR -= 90;
                    break;
                }
                case dir.bottom: {
                    p0 = cc.v2(horizontal_distance, -this.picHeight / 2);
                    p3 = cc.v2(-horizontal_distance, -this.picHeight / 2);
                    angleL -= 180;
                    angleR -= 180;
                    break;
                }
                case dir.left: {
                    p0 = cc.v2(-this.picWidth / 2, -vertical_distance);
                    p3 = cc.v2(-this.picWidth / 2, vertical_distance);
                    angleL -= 270;
                    angleR -= 270;
                    break;
                }
            }
            // console.log('angleL', angleL, angleR)
            let distL = extendLine;
            let distR = extendLine;
            let radianL = -angleL * Math.PI / 180;
            let radianR = -angleR * Math.PI / 180;
            p1 = p0.add(cc.v2(Math.sin(radianL) * distL, Math.cos(radianL) * distL));
            p2 = p3.add(cc.v2(Math.sin(radianR) * distR, Math.cos(radianR) * distR));
            // }
            for (let i = 0; i < this.bezierCount; i++) {
                t = i / (this.bezierCount - 1);
                let pos = this.bezier3(p0, p1, p2, p3, t)

                arr.push(pos);
            }
        }
        // console.log('arr', arr)
        return arr;
    }

    bezier3(p0, p1, p2, p3, t) {
        let t0 = (1 - t) * (1 - t) * (1 - t);
        let t1 = 3 * t * (1 - t) * (1 - t);
        let t2 = 3 * t * t * (1 - t);
        let t3 = t * t * t;
        return p0.mul(t0).add(p1.mul(t1)).add(p2.mul(t2)).add(p3.mul(t3))
    }


    updateColor(comp, color = null) {
        // render data = verts = x|y|u|v|color|x|y|u|v|color|...
        // 填充render data中4个顶点的color部分
        let uintVerts = this._renderData.uintVDatas[0];
        if (!uintVerts) return;
        color = color != null ? color : comp.node.color._val;
        let floatsPerVert = this.floatsPerVert;
        let colorOffset = this.colorOffset;
        for (let i = colorOffset, l = uintVerts.length; i < l; i += floatsPerVert) {
            uintVerts[i] = color;
        }
    }

    /**
     * 1 2
     *  0
     * 4 3
     */

    updateUVs(sprite) {
        let uvs = sprite.spriteFrame.uv;

        let cw = sprite.rootSize.width, ch = sprite.rootSize.height;
        let uvOffset = this.uvOffset;
        let floatsPerVert = this.floatsPerVert;
        let verts = this._renderData.vDatas[0];
        // render data = verts = x|y|u|v|color|x|y|u|v|color|...
        // 填充render data中5个顶点的uv部分
        let local = this._local;
        let posOffset = this.posOffset;
        let uvArr = [];
        // console.log(cw, ch)
        for (let i = 0; i < local.length; i++) {
            let p = local[i];
            let dstOffset = floatsPerVert * i + uvOffset;
            let readPos = p.add(posOffset).add(cc.v2(cw / 2, ch / 2));
            let percentX = readPos.x / cw;
            let percentY = 1 - readPos.y / ch;
            // console.log('readPos', readPos)
            // console.log('posOffset', posOffset)
            // console.log('percentY', percentY)
            let u = percentX;
            let v = percentY;
            verts[dstOffset] = u;
            verts[dstOffset + 1] = v;
            // console.log('verts', u, v)
            // uvArr.push(percentY);
            // uvArr.push(readPos);
            uvArr.push(u, v);
        }

        // console.log('UV坐标', verts);
    }
    // 判断一个点是否在三角形内
    _testInTriangle(point, triA, triB, triC) {
        let AB = triB.sub(triA), AC = triC.sub(triA), BC = triC.sub(triB), AD = point.sub(triA), BD = point.sub(triB);
        return (AB.cross(AC) >= 0 !== AB.cross(AD) < 0)  // D,C 在AB同同方向
            && (AB.cross(AC) >= 0 !== AC.cross(AD) >= 0) // D,B 在AC同同方向
            && (BC.cross(AB) > 0 !== BC.cross(BD) >= 0); // D,A 在BC同同方向
    }
    updateIndices(sprite) {
        // let data = this._renderData;
        // // createFlexData不会填充顶点索引信息，手动补充一下
        // let indices = data.iDatas[0];

        // 计算顶点索引 
        let local = this._local;
        let ids = [];
        const vertexes = [].concat(local);
        // console.log('vertexes', vertexes)
        // 多边形切割，未实现相交的复杂多边形，确保顶点按顺序且围成的线不相交
        let index = 0, rootIndex = -1;
        let totalCross = 0;
        for (let i = 1; i < vertexes.length - 1; i++) {
            let p1 = vertexes[i - 1];
            let p2 = vertexes[i];
            let p3 = vertexes[i + 1];
            let v1 = p2.sub(p1);
            let v2 = p3.sub(p2);
            totalCross += v1.cross(v2);
        }
        // console.log('totalCross', totalCross)
        // 这里通过耳切法来确定索引
        while (vertexes.length >= 3) {
            const p1 = vertexes[index];
            const p2 = vertexes[(index + 1) % vertexes.length];
            const p3 = vertexes[(index + 2) % vertexes.length];

            const v1 = p2.sub(p1);
            const v2 = p3.sub(p2);
            let v = v1.cross(v2);
            let isTu = (v) => {
                let isVTu = false;
                if (totalCross <= 0) {
                    if (v >= 0) {
                        isVTu = true
                    }
                } else {
                    if (v < 0) {
                        isVTu = true
                    }
                }
                // console.log('isVTu', isVTu)
                return isVTu
            }
            // console.log('v', v)
            if (v1.cross(v2) < 0) {
                // 是凸点
                let isIn = false;
                for (const p_t of vertexes) {
                    if (p_t !== p1 && p_t !== p2 && p_t !== p3 && this._testInTriangle(p_t, p1, p2, p3)) {
                        // 其他点在三角形内
                        isIn = true;
                        break;
                    }
                }
                if (!isIn) {
                    // 切耳朵，是凸点，且没有其他点在三角形内
                    ids = ids.concat([local.indexOf(p1), local.indexOf(p2), local.indexOf(p3)]);
                    // ids.push(this.vertexes.indexOf(p1), this.vertexes.indexOf(p2), this.vertexes.indexOf(p3));
                    vertexes.splice(vertexes.indexOf(p2), 1);
                    rootIndex = index;
                    // console.log('找到',);
                } else {
                    index = (index + 1) % vertexes.length;
                    if (index === rootIndex) {
                        // console.log('循环一圈未发现1');
                        break;
                    }
                }
            } else {
                index = (index + 1) % vertexes.length;
                if (index === rootIndex) {
                    // console.log('循环一圈未发现2');
                    break;
                }
            }
            if (index > (vertexes.length - 1)) index = (vertexes.length - 1);
        }
        // console.log('ids', ids);
        let data = this._renderData;
        let indices = data.iDatas[0];

        // data.createFlexData(0, this.verticesFloats, this.indicesCount, this.getVfmt())

        // console.log('ids.length', ids.length);
        // console.log('indices1', indices);
        // let this._renderData.iDatas[0]
        for (let i = 0; i < ids.length; i++) {
            this._renderData.iDatas[0][i] = ids[i];
            indices[i] = ids[i];

            // if (!this.ctx) {
            //     let ctxNode = new cc.Node('ctxNode');
            //     ctxNode.parent = cc.director.getScene();
            //     let ctx = ctxNode.addComponent(cc.Graphics);
            //     this.ctx = ctx;
            // }
            // let lastPoint = null;
            // sprite.scheduleOnce(() => {
            //     if (i < ids.length - 1) {
            //         let pos = local[ids[i]]
            //         let wpos = sprite.node.convertToWorldSpaceAR(pos);
            //         let pos2 = local[ids[i + 1]]
            //         let wpos2 = sprite.node.convertToWorldSpaceAR(pos2);
            //         this.ctx.lineWidth = 2;
            //         this.ctx.moveTo(wpos.x, wpos.y);
            //         this.ctx.lineTo(wpos2.x, wpos2.y);
            //         this.ctx.strokeColor = cc.color(255, 255, 0);
            //         // this.ctx.circle(wpos.x, wpos.y, 5);
            //         this.ctx.stroke();
            //     }
            // }, 0.01 * i)
        }
        this.ids = ids;
        // console.log('索引：', this._renderData.iDatas[0]);

    }
    ids = [];


    updateVerts(sprite: cc.Sprite) {

        let node = sprite.node,
            cw = node.width, ch = node.height,
            appx = node.anchorX * cw, appy = node.anchorY * ch,
            l, b, r, t;
        // 顺时针生成顶点
        let local = this._local;
        let idx = 0;
        let points = [];
        // 上
        local[idx++] = cc.v2(-cw / 2, ch / 2);
        points = this.getInnerPoints(dir.top, this.isTopExtend);
        for (let i = 0; i < points.length; i++) {
            local[idx++] = points[i];
        }
        // 右
        local[idx++] = cc.v2(cw / 2, ch / 2);
        points = this.getInnerPoints(dir.right, this.isRightExtend);
        for (let i = 0; i < points.length; i++) {
            local[idx++] = points[i];
        }
        // 下
        local[idx++] = cc.v2(cw / 2, -ch / 2);
        points = this.getInnerPoints(dir.bottom, this.isBottomExtend);
        for (let i = 0; i < points.length; i++) {
            local[idx++] = points[i];
        }
        // 左
        local[idx++] = cc.v2(-cw / 2, -ch / 2);
        points = this.getInnerPoints(dir.left, this.isLeftExtend);
        for (let i = 0; i < points.length; i++) {
            local[idx++] = points[i];
        }
        // console.log('local', local)

        // if (!this.ctx) {
        //     let ctxNode = new cc.Node('ctxNode');
        //     ctxNode.parent = cc.director.getScene();
        //     let ctx = ctxNode.addComponent(cc.Graphics);
        //     this.ctx = ctx;
        // }
        // for (let i = 0; i < local.length; i++) {
        //     sprite.scheduleOnce(() => {
        //         let pos = local[i]
        //         let wpos = sprite.node.convertToWorldSpaceAR(pos);
        //         this.ctx.fillColor = cc.color(255, 56, 0);
        //         this.ctx.circle(wpos.x, wpos.y, 5);
        //         this.ctx.fill();
        //     }, 0.02 * i)
        // }
        this.updateWorldVerts(sprite);
    }

    updateWorldVerts(comp) {
        if (CC_NATIVERENDERER) {
            this.updateWorldVertsNative(comp);
        } else {
            this.updateWorldVertsWebGL(comp);
        }
    }

    updateWorldVertsWebGL(comp) {
        let local = this._local;
        let verts = this._renderData.vDatas[0];

        let matrix = comp.node._worldMatrix;
        let matrixm = matrix.m,
            a = matrixm[0], b = matrixm[1], c = matrixm[4], d = matrixm[5],
            tx = matrixm[12], ty = matrixm[13];
        /*
        m00 = 1, m01 = 0, m02 = 0, m03 = 0,
        m04 = 0, m05 = 1, m06 = 0, m07 = 0,
        m08 = 0, m09 = 0, m10 = 1, m11 = 0,
        m12 = 0, m13 = 0, m14 = 0, m15 = 1
        */
        // [a,b,c,d] = _worldMatrix[1,2,4,5] == [1,0,0,1]
        // _worldMatrix[12,13]是xy的平移量
        // 即世界矩阵的左上角2x2是单元矩阵，说明在2D场景内没有出现旋转或者缩放
        let justTranslate = a === 1 && b === 0 && c === 0 && d === 1;

        // render data = verts = x|y|u|v|color|x|y|u|v|color|...
        // 填充render data中4个顶点的xy部分
        let index = 0;
        let floatsPerVert = this.floatsPerVert;
        // 4对xy分别乘以 [2,2]仿射矩阵，然后+平移量

        for (let i = 0; i < local.length; i++) {
            let p = local[i];
            // mid
            verts[index] = p.x * a + p.y * c + tx;
            verts[index + 1] = p.x * b + p.y * d + ty;
            // console.log('updateWorldVertsWebGL', p.x, p.y);
            // console.log('updateWorldVertsWebGL2', p.x * a + p.y * c + tx, p.x * b + p.y * d + ty);
            index += floatsPerVert;
        }
    }

    // native场景下使用的updateWorldVerts
    // copy from \jsb-adapter-master\engine\assemblers\assembler-2d.js
    updateWorldVertsNative(comp) {
        let local = this._local;
        let world = this._renderData.vDatas[0];
        let floatsPerVert = this.floatsPerVert;

        let worldIndex = 0;
        for (let i = 0; i < local.length; i++) {
            let p = local[i];
            // mid
            world[worldIndex] = p.x;
            world[worldIndex + 1] = p.y;
            worldIndex += floatsPerVert;
            // console.log('updateWorldVertsNative', p.x, p.y);
        }
    }

    packToDynamicAtlas(comp, frame) {
        if (CC_TEST) return;

        if (!frame._original && cc.dynamicAtlasManager && frame._texture.packable) {
            let packedFrame = cc.dynamicAtlasManager.insertSpriteFrame(frame);
            //@ts-ignore
            if (packedFrame) {
                frame._setDynamicAtlasFrame(packedFrame);
            }
        }
        let material = comp._materials[0];
        if (!material) return;

        if (material.getProperty('texture') !== frame._texture) {
            // texture was packed to dynamic atlas, should update uvs
            comp._vertsDirty = true;
            comp._activateMaterial();
        }
    }
}
